package salvedev

import (
	"encoding/binary"
	"errors"
	"sync"
)

type ModbusSlave struct {
	mu sync.RWMutex

	// 存储区域
	Coils            []bool   // 0x - 可读写线圈
	DiscreteInputs   []bool   // 1x - 只读离散输入
	HoldingRegisters []uint16 // 4x - 可读写保持寄存器
	InputRegisters   []uint16 // 3x - 只读输入寄存器

	// 设备信息
	SlaveID byte
}

func NewModbusSlave(slaveID byte) *ModbusSlave {
	return &ModbusSlave{
		SlaveID:          slaveID,
		Coils:            make([]bool, 65535),
		DiscreteInputs:   make([]bool, 65535),
		HoldingRegisters: make([]uint16, 65535),
		InputRegisters:   make([]uint16, 65535),
	}
}

// 设置线圈状态 (0x)
func (s *ModbusSlave) SetCoil(address uint16, value bool) error {
	if address >= uint16(len(s.Coils)) {
		return errors.New("address out of range")
	}
	s.mu.Lock()
	defer s.mu.Unlock()
	s.Coils[address] = value
	return nil
}

// 批量设置线圈
func (s *ModbusSlave) SetCoils(address uint16, values []bool) error {
	if address+uint16(len(values)) > uint16(len(s.Coils)) {
		return errors.New("address out of range")
	}
	s.mu.Lock()
	defer s.mu.Unlock()
	for i, v := range values {
		s.Coils[address+uint16(i)] = v
	}
	return nil
}

// 设置保持寄存器值 (4x)
func (s *ModbusSlave) SetHoldingRegister(address uint16, value uint16) error {
	if address >= uint16(len(s.HoldingRegisters)) {
		return errors.New("address out of range")
	}
	s.mu.Lock()
	defer s.mu.Unlock()
	s.HoldingRegisters[address] = value
	return nil
}

// 批量设置保持寄存器
func (s *ModbusSlave) SetHoldingRegisters(address uint16, values []uint16) error {
	if address+uint16(len(values)) > uint16(len(s.HoldingRegisters)) {
		return errors.New("address out of range")
	}
	s.mu.Lock()
	defer s.mu.Unlock()
	for i, v := range values {
		s.HoldingRegisters[address+uint16(i)] = v
	}
	return nil
}

// 设置输入寄存器值 (3x)
func (s *ModbusSlave) SetInputRegister(address uint16, value uint16) error {
	if address >= uint16(len(s.InputRegisters)) {
		return errors.New("address out of range")
	}
	s.mu.Lock()
	defer s.mu.Unlock()
	s.InputRegisters[address] = value
	return nil
}

// 批量设置输入寄存器
func (s *ModbusSlave) SetInputRegisters(address uint16, values []uint16) error {
	if address+uint16(len(values)) > uint16(len(s.InputRegisters)) {
		return errors.New("address out of range")
	}
	s.mu.Lock()
	defer s.mu.Unlock()
	for i, v := range values {
		s.InputRegisters[address+uint16(i)] = v
	}
	return nil
}

// 设置离散输入值 (1x)
func (s *ModbusSlave) SetDiscreteInput(address uint16, value bool) error {
	if address >= uint16(len(s.DiscreteInputs)) {
		return errors.New("address out of range")
	}
	s.mu.Lock()
	defer s.mu.Unlock()
	s.DiscreteInputs[address] = value
	return nil
}

// 批量设置离散输入
func (s *ModbusSlave) SetDiscreteInputs(address uint16, values []bool) error {
	if address+uint16(len(values)) > uint16(len(s.DiscreteInputs)) {
		return errors.New("address out of range")
	}
	s.mu.Lock()
	defer s.mu.Unlock()
	for i, v := range values {
		s.DiscreteInputs[address+uint16(i)] = v
	}
	return nil
}

// Modbus异常码
const (
	ExceptionCodeIllegalFunction     = 0x01
	ExceptionCodeIllegalDataAddress  = 0x02
	ExceptionCodeIllegalDataValue    = 0x03
	ExceptionCodeServerDeviceFailure = 0x04
)

// ProcessRequest 处理Modbus请求
func (s *ModbusSlave) ProcessRequest(request []byte) ([]byte, error) {
	if len(request) < 2 {
		return nil, errors.New("invalid request length")
	}

	// 检查从站ID
	if request[0] != s.SlaveID {
		return nil, errors.New("slave ID mismatch")
	}

	functionCode := request[1]

	var response []byte
	var err error

	switch functionCode {
	case 0x01: // 读线圈
		response, err = s.handleReadCoils(request)
	case 0x02: // 读离散输入
		response, err = s.handleReadDiscreteInputs(request)
	case 0x03: // 读保持寄存器
		response, err = s.handleReadHoldingRegisters(request)
	case 0x04: // 读输入寄存器
		response, err = s.handleReadInputRegisters(request)
	default:
		return s.exceptionResponse(functionCode, ExceptionCodeIllegalFunction), nil
	}

	if err != nil {
		return s.exceptionResponse(functionCode, ExceptionCodeIllegalDataAddress), nil
	}

	return response, nil
}

// 异常响应
func (s *ModbusSlave) exceptionResponse(functionCode byte, exceptionCode byte) []byte {
	return []byte{s.SlaveID, functionCode + 0x80, exceptionCode}
}

// 处理读线圈 (0x01)
func (s *ModbusSlave) handleReadCoils(request []byte) ([]byte, error) {
	if len(request) != 6 {
		return nil, errors.New("invalid request length")
	}

	startAddr := binary.BigEndian.Uint16(request[2:4])
	quantity := binary.BigEndian.Uint16(request[4:6])

	if quantity < 1 || quantity > 2000 {
		return nil, errors.New("invalid quantity")
	}

	s.mu.RLock()
	defer s.mu.RUnlock()

	byteCount := (quantity + 7) / 8
	response := make([]byte, 3+byteCount)
	response[0] = s.SlaveID
	response[1] = 0x01
	response[2] = byte(byteCount)

	for i := uint16(0); i < quantity; i++ {
		addr := startAddr + i
		if addr >= uint16(len(s.Coils)) {
			return nil, errors.New("address out of range")
		}
		if s.Coils[addr] {
			response[3+i/8] |= 1 << (i % 8)
		}
	}

	return response, nil
}

// 处理读离散输入 (0x02)
func (s *ModbusSlave) handleReadDiscreteInputs(request []byte) ([]byte, error) {
	if len(request) != 6 {
		return nil, errors.New("invalid request length")
	}

	startAddr := binary.BigEndian.Uint16(request[2:4])
	quantity := binary.BigEndian.Uint16(request[4:6])

	// Modbus协议规定最多读取2000个离散输入
	if quantity < 1 || quantity > 2000 {
		return nil, errors.New("invalid quantity, must be between 1 and 2000")
	}

	s.mu.RLock()
	defer s.mu.RUnlock()

	// 计算需要的字节数 (每个字节可以存储8个离散输入状态)
	byteCount := (quantity + 7) / 8
	response := make([]byte, 3+byteCount)
	response[0] = s.SlaveID
	response[1] = 0x02
	response[2] = byte(byteCount)

	// 将离散输入状态打包到响应字节中
	for i := uint16(0); i < quantity; i++ {
		addr := startAddr + i
		if addr >= uint16(len(s.DiscreteInputs)) {
			return nil, errors.New("address out of range")
		}
		if s.DiscreteInputs[addr] {
			response[3+i/8] |= 1 << (i % 8) // 设置对应位
		}
	}

	return response, nil
}

// 处理读保持寄存器 (0x03)
func (s *ModbusSlave) handleReadHoldingRegisters(request []byte) ([]byte, error) {
	if len(request) != 6 {
		return nil, errors.New("invalid request length")
	}

	startAddr := binary.BigEndian.Uint16(request[2:4])
	quantity := binary.BigEndian.Uint16(request[4:6])

	if quantity < 1 || quantity > 125 {
		return nil, errors.New("invalid quantity")
	}

	s.mu.RLock()
	defer s.mu.RUnlock()

	response := make([]byte, 3+2*quantity)
	response[0] = s.SlaveID
	response[1] = 0x03
	response[2] = byte(2 * quantity)

	for i := uint16(0); i < quantity; i++ {
		addr := startAddr + i
		if addr >= uint16(len(s.HoldingRegisters)) {
			return nil, errors.New("address out of range")
		}
		binary.BigEndian.PutUint16(response[3+2*i:], s.HoldingRegisters[addr])
	}

	return response, nil
}

// 处理读输入寄存器 (0x04)
func (s *ModbusSlave) handleReadInputRegisters(request []byte) ([]byte, error) {
	if len(request) != 6 {
		return nil, errors.New("invalid request length")
	}

	startAddr := binary.BigEndian.Uint16(request[2:4])
	quantity := binary.BigEndian.Uint16(request[4:6])

	// Modbus协议规定最多读取125个输入寄存器
	if quantity < 1 || quantity > 125 {
		return nil, errors.New("invalid quantity, must be between 1 and 125")
	}

	s.mu.RLock()
	defer s.mu.RUnlock()

	// 每个寄存器占2个字节
	response := make([]byte, 3+2*quantity)
	response[0] = s.SlaveID
	response[1] = 0x04
	response[2] = byte(2 * quantity)

	// 将输入寄存器值填充到响应中
	for i := uint16(0); i < quantity; i++ {
		addr := startAddr + i
		if addr >= uint16(len(s.InputRegisters)) {
			return nil, errors.New("address out of range")
		}
		binary.BigEndian.PutUint16(response[3+2*i:], s.InputRegisters[addr])
	}

	return response, nil
}
